package com.commerce.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.commerce.common.utils.PageResult;
import com.commerce.common.entity.Category;
import com.commerce.product.mapper.CategoryMapper;
import com.commerce.product.service.CategoryService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    private static final String CATEGORY_CACHE_KEY_PREFIX = "category:";
    private static final String CATEGORY_LIST_CACHE_KEY = "category:list";
    private static final String CATEGORY_PAGE_CACHE_KEY = "category:page:";
    private static final long CACHE_EXPIRE_TIME = 60; // 分类缓存时间设置为60分钟，因为分类不经常变动

    private String generatePageCacheKey(Integer pageNum, Integer pageSize) {
        return CATEGORY_PAGE_CACHE_KEY + "p" + pageNum + ":s" + pageSize;
    }

    @Override
    public Page<Category> getCategoryPage(Integer pageNum, Integer pageSize) {
        // 在Controller中，前端传入page=1时，后端实际查询的是第0页
        // 所以需要在生成缓存键时，保持与前端一致，使用原始的pageNum
        String cacheKey = generatePageCacheKey(pageNum, pageSize);
        String recordsKey = cacheKey + ":records";
        String totalKey = cacheKey + ":total";
        
        try {
//            log.info("尝试从缓存获取分页数据: {}", cacheKey);
            
            // 尝试从缓存中获取数据
            List<Category> cachedRecords = (List<Category>) redisTemplate.opsForValue().get(recordsKey);
            Object cachedTotal = redisTemplate.opsForValue().get(totalKey);
            
            if (cachedRecords != null && cachedTotal != null) {
//                log.info("从缓存中获取到数据，records大小: {}", cachedRecords.size());
                Page<Category> cachedPage = new Page<>(pageNum, pageSize);
                cachedPage.setRecords(cachedRecords);
                cachedPage.setTotal(Long.parseLong(cachedTotal.toString()));
                return cachedPage;
            }

//            log.info("缓存未命中，从数据库查询");
            // 如果缓存中没有，从数据库查询
            Page<Category> page = new Page<>(pageNum, pageSize);
            page = this.page(page);
            
            // 将结果存入缓存
            if (page != null && page.getRecords() != null) {
                redisTemplate.opsForValue().set(recordsKey, page.getRecords(), CACHE_EXPIRE_TIME, TimeUnit.MINUTES);
                redisTemplate.opsForValue().set(totalKey, page.getTotal(), CACHE_EXPIRE_TIME, TimeUnit.MINUTES);
//                log.info("查询结果已存入缓存，records大小: {}, 缓存键: {}", page.getRecords().size(), recordsKey);
            }
            
            return page;
        } catch (Exception e) {
//            log.error("获取分类分页数据失败: {}", e.getMessage(), e);
            return new Page<>();
        }
    }

    @Override
    public List<Category> list() {
        try {
//            log.debug("尝试从缓存获取所有分类数据");
            // 尝试从缓存中获取所有分类数据
            List<Category> cachedCategories = (List<Category>) redisTemplate.opsForValue().get(CATEGORY_LIST_CACHE_KEY);
            if (cachedCategories != null) {
//                log.debug("从缓存中获取到分类数据，大小: {}", cachedCategories.size());
                return cachedCategories;
            }

//            log.debug("缓存未命中，从数据库查询所有分类");
            // 如果缓存中没有，从数据库查询
            List<Category> categories = super.list();
            
            // 将结果存入缓存
            if (categories != null && !categories.isEmpty()) {
                redisTemplate.opsForValue().set(CATEGORY_LIST_CACHE_KEY, categories, CACHE_EXPIRE_TIME, TimeUnit.MINUTES);
//                log.debug("分类数据已存入缓存，大小: {}", categories.size());
            }
            
            return categories;
        } catch (Exception e) {
//            log.error("获取所有分类数据失败: {}", e.getMessage(), e);
            return new ArrayList<>();
        }
    }

    @Override
    public boolean save(Category category) {
        boolean result = super.save(category);
        if (result) {
            clearCategoryCache(null);  // 新增时不需要传入ID
        }
        return result;
    }

    @Override
    public boolean updateById(Category category) {
        boolean result = super.updateById(category);
        if (result) {
            clearCategoryCache(category.getId());
        }
        return result;
    }


    public boolean removeById(Long id) {
        boolean result = super.removeById(id);
        if (result) {
            clearCategoryCache(id);
        }
        return result;
    }

    // 清除分类相关的所有缓存
    private void clearCategoryCache(Long categoryId) {
        try {
//            log.info("开始清理分类缓存");
            
            // 1. 清除单个分类缓存
            if (categoryId != null) {
                String categoryKey = CATEGORY_CACHE_KEY_PREFIX + categoryId;
                redisTemplate.delete(categoryKey);
//                log.info("清除单个分类缓存: {}", categoryKey);
            }

            // 2. 清除分类列表缓存
            Boolean deletedCount1 = redisTemplate.delete(CATEGORY_LIST_CACHE_KEY);
//            log.info("清除分类列表缓存: {}, 结果: {}", CATEGORY_LIST_CACHE_KEY, deletedCount1);
            
            // 3. 使用更广泛的模式匹配所有分类相关的缓存
            Set<String> allCategoryKeys = redisTemplate.keys(CATEGORY_CACHE_KEY_PREFIX + "*");
            if (allCategoryKeys != null && !allCategoryKeys.isEmpty()) {
                Long deletedCount2 = redisTemplate.delete(allCategoryKeys);
//                log.info("清除所有分类相关缓存: {} 个键, 成功删除: {} 个", allCategoryKeys.size(), deletedCount2);
                
                // 打印被删除的键，方便调试
                for (String key : allCategoryKeys) {
//                    log.info("删除缓存键: {}", key);
                }
            } else {
//                log.info("未找到其他分类相关的缓存键");
            }
            
            // 4. 使用更广泛的模式匹配所有分页相关的缓存
            Set<String> pageKeys = redisTemplate.keys(CATEGORY_PAGE_CACHE_KEY + "*");
            if (pageKeys != null && !pageKeys.isEmpty()) {
                Long deletedCount3 = redisTemplate.delete(pageKeys);
//                log.info("清除分页相关缓存: {} 个键, 成功删除: {} 个", pageKeys.size(), deletedCount3);
                
                // 打印被删除的键，方便调试
                for (String key : pageKeys) {
//                    log.info("删除缓存键: {}", key);
                }
            } else {
//                log.info("未找到分页相关的缓存键");
            }
            
//            log.info("分类缓存清理完成");
        } catch (Exception e) {
//            log.error("清除分类缓存失败: {}", e.getMessage(), e);
        }
    }

    @Override
    public List<Category> getAllCategories() {
        LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Category::getStatus, 1)
               .orderByAsc(Category::getSort);
        return list(wrapper);
    }

    @Override
    public PageResult<Category> getPageList(int page, int size) {
        Page<Category> pageParam = new Page<>(page, size);
        LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByAsc(Category::getSort);
        
        Page<Category> pageResult = page(pageParam, wrapper);
        return PageResult.of(pageResult.getRecords(), pageResult.getTotal(), page, size);
    }

    @Override
    public Category getCategoryById(Long id) {
        return getById(id);
    }

    @Override
    public boolean addCategory(Category category) {
        category.setCreateTime(LocalDateTime.now());
        category.setUpdateTime(LocalDateTime.now());
        return save(category);
    }

    @Override
    public boolean updateCategory(Category category) {
        category.setUpdateTime(LocalDateTime.now());
        return updateById(category);
    }

    @Override
    public boolean deleteCategory(Long id) {
        return removeById(id);
    }
} 